package framework.fastdevelop.com.touch.group;

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.OverScroller;

import com.orhanobut.logger.Logger;

import java.util.Arrays;

public class HorizontalScroll extends ViewGroup {

    //最后一次按下的x坐标（onTouchEvent）
    private int mLastDownX;

    //最后一次按下的x、y坐标（onInterceptTouchEvent）
    private int mInterceptLastDownX;
    private int mInterceptLastDownY;

    private OverScroller mOverScroller;

    private VelocityTracker mVelocityTracker;

    //最小速度
    private int mMinVelocity;
    //最大速度
    private int mMaxVelocity;

    static final float DEFAULT_MAX_SLOP = 0.5f, //滑动超出边界后的阻力
            DEFAULT_MIN_SLOP = 0.2f;//滑动超出边界后的最小阻力

    static final int SCROLL_BACK_MILLIS = 400, //滑动超出边界后，弹回至正常坐标需要的时间
            OVER_SCROLL_VAL = 50;//fling状态下的滑动可以超出多少坐标

    private float mSlop = DEFAULT_MAX_SLOP;

    //最小可fling的x坐标
    private int mMinScrollX;
    //最大可fling的x坐标
    private int mMaxScrollX;

    //记录多个手指touch id
    private int mPointerId;

    public HorizontalScroll(Context context) {
        this(context, null);
    }

    public HorizontalScroll(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    @TargetApi(Build.VERSION_CODES.M)
    public HorizontalScroll(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        mOverScroller = new OverScroller(context);
        //设置滚动摩擦阻力
        //mOverScroller.setFriction(0.05f);

        mMinVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
        mMaxVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();

        setBackgroundColor(Color.DKGRAY);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);

        int resultGroupWidth = 0, resultGroupHeight = 0;

        int[] maxHeights = new int[getChildCount()];
        //先测量child，在根据child来定义自己的宽高
        for (int i = 0, size = getChildCount(); i < size; i++) {
            View childView = getChildAt(i);
            if (childView.getVisibility() != GONE) {

                //考虑child的margin
                LayoutParams params = (LayoutParams) childView.getLayoutParams();

                measureChildWithMargins(childView,widthMeasureSpec,0,heightMeasureSpec,0);
                //measureChild(childView, widthMeasureSpec, heightMeasureSpec);

                resultGroupWidth += childView.getMeasuredWidth() + params.leftMargin + params.rightMargin;
                maxHeights[i] = childView.getMeasuredHeight() + params.topMargin + params.bottomMargin;
            }
        }

        //考虑parent的padding
        resultGroupWidth += getPaddingLeft() + getPaddingRight();

        //筛选出最高的高度
        Arrays.sort(maxHeights);
        resultGroupHeight = maxHeights[maxHeights.length - 1];


        if (modeWidth == MeasureSpec.AT_MOST && modeHeight == MeasureSpec.AT_MOST) {
            setMeasuredDimension(resultGroupWidth, resultGroupHeight);
        } else if (modeWidth == MeasureSpec.AT_MOST) {
            setMeasuredDimension(resultGroupWidth, sizeHeight);
        } else if (modeHeight == MeasureSpec.AT_MOST) {
            setMeasuredDimension(sizeWidth, resultGroupHeight);
        } else {
            setMeasuredDimension(sizeWidth, sizeHeight);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int appendGroupWidth = 0;
        for (int i = 0, size = getChildCount(); i < size; i++) {
            View childView = getChildAt(i);

            //考虑child的margin
            LayoutParams params = (LayoutParams) childView.getLayoutParams();

            //考虑child的margin和parent的padding
            childView.layout(appendGroupWidth + params.leftMargin + getPaddingLeft(), params.topMargin + getPaddingTop(), appendGroupWidth + childView.getMeasuredWidth() + params.rightMargin, childView.getMeasuredHeight() + params.bottomMargin);

            appendGroupWidth += childView.getMeasuredWidth();
        }

        //最小可fling的x坐标是0
        mMinScrollX = 0;
        //最大可fling的x坐标是所有子view的宽度减去父容器的宽度(ChildTotalWidth - getWidth)
        mMaxScrollX = appendGroupWidth -= getWidth();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean intercept = false;
        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mPointerId = ev.getPointerId(0);

                intercept = false;

                mInterceptLastDownX = (int) ev.getX();
                mInterceptLastDownY = (int) ev.getY();

                if (!mOverScroller.isFinished()) {
                    mOverScroller.abortAnimation();
                    intercept = true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                int pointerIndex = ev.findPointerIndex(mPointerId);

                int dx = Math.abs((int) (ev.getX(pointerIndex) - mInterceptLastDownX));
                int dy = Math.abs((int) (ev.getY(pointerIndex) - mInterceptLastDownY));

                if (dx > dy + 25) {
                    //交给自己的onTouchEvent处理
                    intercept = true;
                } else {
                    //交给子View处理
                    intercept = false;
                }

                mInterceptLastDownX = (int) ev.getX(pointerIndex);
                mInterceptLastDownY = (int) ev.getY(pointerIndex);

                /*
                当拦截后不会触发onTouchEvent的ACTION_DOWN事件
                所以在这里记录一下最后的一次拦截X坐标
                给onTouchEvent的ACTION_MOVE使用
                 */
                mLastDownX = (int) ev.getX(pointerIndex);

                break;
            case MotionEvent.ACTION_UP:
                intercept = false;
                break;
        }

        return intercept;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mVelocityTracker == null)
            mVelocityTracker = VelocityTracker.obtain();

        mVelocityTracker.addMovement(event);

        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mPointerId = event.getPointerId(0);

                if (!mOverScroller.isFinished()) {
                    mOverScroller.abortAnimation();
                }

                mLastDownX = (int) event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                int pointerIndex = event.findPointerIndex(mPointerId);

                int dx = (int) (event.getX(pointerIndex) - mLastDownX);

                //滑动超出边界后，在ACTION_MOVE状态下持续的减去0.01，直到mSlop小于规定的最小阻力，恢复成最小阻力
                mSlop -= 0.01;
                if (mSlop < DEFAULT_MIN_SLOP)
                    mSlop = DEFAULT_MIN_SLOP;

                if (getScrollX() >= 0 && getScrollX() <= mMaxScrollX) {
                    //滑动没有超出边界
                    scrollBy(-dx, 0);
                } else {
                    //滑动超出边界
                    scrollBy((int) (-dx * mSlop), 0);
                }

                mLastDownX = (int) event.getX(pointerIndex);
                break;
            case MotionEvent.ACTION_UP:
                //抬起所有手指的时候恢复滑动阻力为规定的最大值
                mSlop = DEFAULT_MAX_SLOP;

                mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);

                int velocity = -(int) mVelocityTracker.getXVelocity(mPointerId);

                if (getScrollX() >= 0 && getScrollX() <= mMaxScrollX) {
                    //正常范围内
                    if (Math.abs(velocity) > mMinVelocity) {
                        mOverScroller.fling(getScrollX(), 0, velocity, 0, mMinScrollX, mMaxScrollX, 0, 0, OVER_SCROLL_VAL, 0);
                        invalidate();
                    }
                } else {
                    //超出边界
                    boolean isLeft = getScrollX() <= getLeft();

                    int scrollerDx = isLeft ? Math.abs(getScrollX() - getLeft() + ((MarginLayoutParams)getLayoutParams()).leftMargin) : -Math.abs(getScrollX() - mMaxScrollX);

                    mOverScroller.startScroll(getScrollX(), 0, scrollerDx, 0, SCROLL_BACK_MILLIS);
                    invalidate();
                }

                mVelocityTracker.recycle();
                mVelocityTracker = null;
                break;
            case MotionEvent.ACTION_POINTER_UP:
                int pointerIndexLeave = event.getActionIndex();

                int pointerIdLeave = event.getPointerId(pointerIndexLeave);

                if (mPointerId == pointerIdLeave) {
                    int reIndex = pointerIndexLeave == 0 ? 1 : 0;

                    mPointerId = event.getPointerId(reIndex);
                    mLastDownX = (int) event.getX(reIndex);
                    //if (mVelocityTracker != null)
                    //mVelocityTracker.clear();
                }
                break;
        }
        return true;
    }

    @Override
    public void computeScroll() {
        if (mOverScroller.computeScrollOffset()) {
            scrollTo(mOverScroller.getCurrX(), 0);
            postInvalidate();
        }
    }

    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new LayoutParams(p);
    }

    @Override
    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }

    @Override
    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

    public static class LayoutParams extends MarginLayoutParams {

        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
        }

        public LayoutParams(int width, int height) {
            super(width, height);
        }

        public LayoutParams(MarginLayoutParams source) {
            super(source);
        }

        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }
    }
}
